/*
* Copyright (c) 2006 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_EVENT_H
#define M_EVENT_H

#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#include <sys/timeb.h>
#endif

#ifndef INFINITE
#define INFINITE            0xFFFFFFFF  // Infinite timeout
#endif

namespace Engine
{
#ifdef WIN32
/**
	*   Windows event specific implementation.
	*/
	class Event
	{
	public:
		Event()
		{
			// Manual reset since multiple threads can wait on this
			m_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
			if(m_hEvent == NULL)
				throw std::logic_error("Can't create win32 event.");
		}

		~Event()
		{
			if(m_hEvent)
				CloseHandle(m_hEvent);
		}

		void Signal()
		{
			if(m_hEvent)
				::SetEvent(m_hEvent);
		}

		void Reset()
		{
			::ResetEvent(m_hEvent);
		}

		void Wait(unsigned long time)
		{
			WaitForSingleObject(m_hEvent,time);
		}

	private:
		HANDLE m_hEvent;
	};
#else
	/**
	*   pthreads event specific implementation.
	*/
	class Event
	{
	public:
		Event()
		{
			pthread_mutexattr_t attr;
			pthread_mutexattr_init(&attr);
			pthread_mutex_init(&m_mutex, &attr);
			pthread_mutexattr_destroy(&attr);
			pthread_cond_init(&m_cond, NULL);
			m_signaled = false;
			m_count = 0;
		}

		~Event()
		{
			pthread_cond_destroy(&m_cond);
			pthread_mutex_destroy(&m_mutex);
		}

		void Signal()
		{
			pthread_mutex_lock(&m_mutex);
			m_signaled = true;
			++m_count;
			pthread_cond_broadcast(&m_cond);
			pthread_mutex_unlock(&m_mutex);
		}

		void Reset()
		{
			m_signaled = false;
		}

		void Wait(unsigned long time)
		{
			const unsigned long NANOSEC_PER_MILLISEC = 1000000;
			int	rc = 0;
			timespec spec;
			timeb currSysTime;
			ftime(&currSysTime);

			spec.tv_sec = static_cast<long>(currSysTime.time);
			spec.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
			spec.tv_nsec += time * NANOSEC_PER_MILLISEC;

			pthread_mutex_lock(&m_mutex);
			long count = m_count;
			while(!m_signaled && m_count == count)
			{
				if(time != INFINITE)
					rc = pthread_cond_timedwait(&m_cond, &m_mutex, &spec);
				else
					pthread_cond_wait(&m_cond, &m_mutex);
				if(rc != 0)
					break;
			}
			pthread_mutex_unlock(&m_mutex);
		}

	private:
		pthread_mutex_t m_mutex;
		pthread_cond_t m_cond;
		bool m_signaled;
		int m_count;
	};
#endif
}

#endif
